#include "KPackage.h"
#include "KDebug.h"
#include <cstring>

KPackage::KPackage(size_t size) : m_len(0), m_size(size)
{
    assert(size > 0);
    m_data.reset(new uint8_t[m_size]);
}

KPackage::KPackage(const KPackage &pack) :
    m_data(new uint8_t[pack.m_size]),
    m_len(pack.m_len),
    m_size(pack.m_size)
{
    memcpy(m_data, pack.m_data, m_len);
}
    
KPackage &KPackage::operator=(const KPackage &pack)
{
    if (this != &pack) {
        m_len = pack.m_len;
        m_size = pack.m_size;
        m_data.reset(new uint8_t[m_size]);
        memcpy(m_data, pack.m_data, m_len);
    }
    return *this;
}

KPackage &KPackage::append(const void *src, size_t len)
{
    if (src != NULL && len > 0) {
        resize(m_len + len);
        memmove(m_data + m_len, src, len);
        m_len += len;
    }
    return *this;
}

KPackage &KPackage::append(const KPackage &pack)
{
    return append(pack.m_data, pack.m_len);
}

KPackage &KPackage::appendNothing(size_t len)
{
    if (len > 0) {
        resize(m_len + len);
        m_len += len;
    }
    return *this;
}

void KPackage::clear()
{
    m_len = 0;
}

void KPackage::fill(uint8_t val)
{
    memset(m_data, val, m_size);
}

bool KPackage::find(size_t &pos, const void *val, size_t len) const
{
    if (val && len > 0) {
        for (; pos + len <= m_len; ++pos) {
            if (memcmp(m_data + pos, val, len) == 0) {
                return true;
            }
        }
    }
    return false;
}

bool KPackage::qfind(size_t &pos, const void *val, size_t len) const
{
    // Using Knuth-Morris-Pratt Algorithm
    if (val && len > 0) {
        if (pos + len <= m_len) {
            const uint8_t *pat = reinterpret_cast<const uint8_t *>(val);
            // Preprocessing...
            KAutoPtr<int, true> next(new int[len]);
            size_t  i = 0;
            int     j = -1;
            next[i] = j;
            while (i + 1 < len) {
                if (j < 0 || pat[i] == pat[j]) {
                    next[++i] = ++j;
                } else {
                    j = next[j];
                }
            }
            // Searching...
            i = pos;
            j = 0;
            do {
                if (j == len) {
                    pos = i - j;
                    return true;
                } else if (j < 0 || m_data[i] == pat[j]) {
                    ++i, ++j;
                } else {
                    j = next[j];
                }
            } while (i < m_len);
        }
    }
    return false;
}

KPackage &KPackage::rewind(size_t len)
{
    if (m_len <= len) {
        m_len = 0;
    } else {
        m_len -= len;
    }
    return *this;
}

KPackage &KPackage::rewindTo(size_t len)
{
    if (m_len > len) {
        m_len = len;
    }
    return *this;
}

void KPackage::shrink(size_t beg, bool move)
{
    if (move) {
        if (m_len <= beg) {
            m_len = 0;
        } else {
            m_len -= beg;
            memmove(m_data, m_data + beg, m_len);
        }
    } else {
        KDebugLib("KPackage: resize from %d", m_size);
        if (m_len <= beg) {
            m_len = 0;
            m_size = KPackage::DefaultSize;
            m_data.reset(new uint8_t[m_size]);
        } else {
            m_len -= beg;
            m_size = m_len;
            KAutoPtr<uint8_t, true> buf(new uint8_t[m_size]);
            memcpy(buf, m_data + beg, m_len);
            m_data.reset(buf.release());
        }
        KDebugLib("KPackage: resize to %d", m_size);
    }
}

void KPackage::resize(size_t size)
{
    if (m_size < size) {
        KDebugLib("KPackage: resize from %d", m_size);
        do {
            m_size <<= 1;
        } while (m_size < size);
        KAutoPtr<uint8_t, true> buf(new uint8_t[m_size]);
        memcpy(buf, m_data, m_len);
        m_data.reset(buf.release());
        KDebugLib("KPackage: resize to %d", m_size);
    }
}